package com.tian.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import com.tian.asyn.InvitedUserRecordAsynchronous;
import com.tian.config.RedisConfig;
import com.tian.convert.DataConvertUtil;
import com.tian.dto.InvitedDto;
import com.tian.dto.QueryUserReqDto;
import com.tian.dto.QueryUserRespDto;
import com.tian.dto.user.*;
import com.tian.entity.GlobalProperty;
import com.tian.entity.User;
import com.tian.enums.BusiTypeEnum;
import com.tian.enums.InsertUpdateDateBaseFlagEnum;
import com.tian.enums.ResultCode;
import com.tian.mapper.UserMapper;
import com.tian.service.SendCodeService;
import com.tian.service.UserService;
import com.tian.util.*;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * @author tianwc  公众号：java后端技术全栈、面试专栏
 * @version 1.0.0
 * 博客地址：<a href="http://woaijava.cc/">博客地址</a>
 * <p>
 * 用户注册、邀请用户注册以及信息管理
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private RedissonClient redissonClient;
    @Resource
    private RedisConfig redisConfig;
    @Resource
    private SendCodeService sendCodeService;
    @Resource
    private InvitedUserRecordAsynchronous invitedUserRecordAsynchronous;

    @Override
    public CommonResult<UsersRespDto> selectById(Integer id) {
        User user = userMapper.getUserById(id);
        if (user == null) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED);
        }
        return CommonResult.success(UsersConvert.convert4SelectById(user));
    }


    private CommonResult<User> doRegister(String phone, String password, Date createTime) {
        User user = new User();
        if (StringUtil.isBlank(password)) {
            user.setUserPassword(ConstantUtil.DEFAULT_PASSWORD);
        }
        //由运营人员配置多少注册成功后送多少积分
        String cache = redisConfig.get(RedisConstantPre.GLOBAL_PROPERTY_PRE + BusiTypeEnum.REGISTER_TO_POINTS);
        List<GlobalProperty> globalPropertyList = JSON.parseArray(cache, GlobalProperty.class);
        int points = 0;
        if (!CollectionUtil.isEmpty(globalPropertyList)) {
            points = globalPropertyList.get(0).getBusiDefault();
        }
        user.setPhone(phone);
        user.setPoints(points);
        user.setCreateTime(createTime);
        user.setNickName(ConstantUtil.DEFAULT_NICK+ RandomUtil.randomNumbers(4));
        user.setStatus(0);
        user.setUserType(0);
        user.setInvitedCode( RandomUtil.randomNumbers(8));
        userMapper.insertUser(user);
        return CommonResult.failed(ResultCode.FAILED);
    }

    /**
     * 在同一个事务中完成：
     * 1、更新邀请用户，邀请人获取收益，累加到可用余额中
     * 2、增加邀请人记录
     * 3、增加邀请人收益记录
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public CommonResult<User> register(InvitedDto invitedDto, String codeCachePre) {
        //验证码校验
        CommonResult<Boolean> codeValidResult = sendCodeService.codeValid(invitedDto.getPhone(), invitedDto.getCode(), codeCachePre);
        if (!codeValidResult.getData()) {
            return CommonResult.failed(codeValidResult.getCode(), codeValidResult.getMessage());
        }
        Date createTime = new Date();
        //普通注册（非邀请注册）
        if (StringUtil.isBlank(invitedDto.getInvitedCode())) {
            CommonResult<User> resultRegister = doRegister(invitedDto.getPhone(), invitedDto.getPassword(), createTime);
            //用户注册失败
            if (resultRegister.getCode() != ResultCode.SUCCESS.getCode()) {
                return CommonResult.failed(resultRegister.getCode(), resultRegister.getMessage());
            }
            return resultRegister;
        }
        //参数非空校验  唯一索引
        User user = userMapper.selectByInvitationCode(invitedDto.getInvitedCode());
        if (user == null) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED);
        }

        String key = RedisConstantPre.INVITED_USER_REGISTER_PRE + user.getId();
        RLock rLock = redissonClient.getLock(key);
        try {
            //再查一次
            user = userMapper.getUserById(user.getId());
            CommonResult<User> resultRegister = doRegister(invitedDto.getPhone(), invitedDto.getPassword(), createTime);
            //用户注册失败
            if (resultRegister.getCode() != ResultCode.SUCCESS.getCode()) {
                return CommonResult.failed(resultRegister.getCode(), resultRegister.getMessage());
            }
            //异步落库 邀请记录
            invitedUserRecordAsynchronous.insert(user.getId(), createTime, resultRegister.getData().getId());

            //更新邀请人可用余额
            String cache = redisConfig.get(RedisConstantPre.GLOBAL_PROPERTY_PRE + BusiTypeEnum.INVITED_REGISTER_TO_BALANCE);
            List<GlobalProperty> globalPropertyList = JSON.parseArray(cache, GlobalProperty.class);
            int income = 0;
            if (!CollectionUtil.isEmpty(globalPropertyList)) {
                income = globalPropertyList.get(0).getBusiDefault();
            }
            user.setMoney(user.getMoney().add(new BigDecimal(income / 100)));
            userMapper.updateUser(user);
        } finally {
            rLock.unlock();
        }
        return CommonResult.success(user);
    }

    @Override
    public CommonResult<QueryUserRespDto> selectByPhone(QueryUserReqDto queryUserReqDto) {
        if (StringUtil.isBlank(queryUserReqDto.getPhone())) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, "手机号为空");
        }
        User user = userMapper.selectByPhone(queryUserReqDto.getPhone());
        if (user == null) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED);
        }
        return CommonResult.success(DataConvertUtil.convert(user));
    }

    @Override
    public CommonResult<UserLoginResDto> login(UserLoginReqDto userLoginReqDto, String codeCachePre) {
        if (StringUtil.isBlank(userLoginReqDto.getPhone())) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, "手机号为空");
        }
        if (StringUtil.isBlank(userLoginReqDto.getCode())) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, "验证码为空");
        }
        CommonResult<Boolean> validResult = sendCodeService.codeValid(userLoginReqDto.getPhone(), userLoginReqDto.getCode(), RedisConstantPre.SEND_CODE_LOGIN_PRE);
        if (!validResult.getData()) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, "验证码错误");
        }
        User user = userMapper.selectByPhone(userLoginReqDto.getPhone());
        if (user == null) {
            InvitedDto invitedDto = new InvitedDto();
            invitedDto.setPhone(userLoginReqDto.getPhone());
            invitedDto.setCode(userLoginReqDto.getCode());
            CommonResult<User> result = register(invitedDto, RedisConstantPre.SEND_CODE_LOGIN_PRE);
            if (result.getCode() == ResultCode.SUCCESS.getCode()) {
                return CommonResult.failed(result.getCode(), result.getMessage());

            }
            user = result.getData();
        }
        String token = TokenUtils.genToken(user.getId().toString(), user.getUserPassword());
        UserLoginResDto userLoginResDto = new UserLoginResDto();
        userLoginResDto.setNickName(user.getNickName());
        userLoginResDto.setUserId(user.getId());
        userLoginResDto.setToken(token);
        userLoginResDto.setAvatarUrl(user.getAvatarUrl());
        userLoginResDto.setInvitedCode(user.getInvitedCode());
        redisConfig.set(token, JSON.toJSONString(user));
        return CommonResult.success(userLoginResDto);
    }

    @Override
    public DataGridView list(UserQueryListReqDto userQueryListReqDto) {

        int total = userMapper.count(userQueryListReqDto);
        if (total == 0) {
            return new DataGridView(0L, null);
        }
        userQueryListReqDto.setStart((userQueryListReqDto.getPage() - 1) * userQueryListReqDto.getLimit());
        List<User> userList = userMapper.page(userQueryListReqDto);

        return new DataGridView(total, userList);
    }

    @Override
    public void removeById(Integer id) {
        int flag = userMapper.removeById(id);
        if (InsertUpdateDateBaseFlagEnum.SUCCESS.getFlag() != flag) {
            throw new RuntimeException("设置用户id=" + id + "为不可用失败");
        }
    }

    @Override
    public CommonResult<User> update(InvitedDto invitedDto, String sendCodeRegisterPre) {
        User user = UserCacheUtil.getUser();
        user= userMapper.getUserById(user.getId());
        user.setAvatarUrl(invitedDto.getAvatarUrl());

        user.setBirthday(invitedDto.getBirthday());
        user.setRealName(invitedDto.getRealName());

        user.setEmail(invitedDto.getEmail());
        user.setIdNo(invitedDto.getIdNo());
        user.setGender(invitedDto.getGender());

        user.setProvince(invitedDto.getProvince());
        user.setCity(invitedDto.getCity());
        user.setArea(invitedDto.getArea());
        user.setDetailAddress(invitedDto.getDetailAddress());

        userMapper.updateUser(user);
        return CommonResult.success();
    }

    @Override
    public CommonResult<User> updatePassword(UserUpdatePasswordReqDto userUpdatePasswordReqDto, String sendCodePasswordPre) {
        User user = UserCacheUtil.getUser();
        if (StringUtil.isBlank(userUpdatePasswordReqDto.getNewPassword())) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, "新密码为空");
        }
        if (StringUtil.isBlank(userUpdatePasswordReqDto.getCode())) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, "验证码为空");
        }
        CommonResult<Boolean> validResult = sendCodeService.codeValid(user.getPhone(), userUpdatePasswordReqDto.getCode(), RedisConstantPre.SEND_CODE_PASSWORD_PRE);
        if (!validResult.getData()) {
            return CommonResult.failed(ResultCode.VALIDATE_FAILED, "验证码错误");
        }
        user= userMapper.getUserById(user.getId());
        user.setUserPassword(userUpdatePasswordReqDto.getNewPassword());
        userMapper.updateUser(user);
        return CommonResult.success();
    }
}
